#ifdef STM32F3xx

#include <logger_rte.h>
#include <sys/unistd.h>
#include <stm32f3xx_ll_usart.h>
#include <stm32f3xx_ll_adc.h>
#include "boardbase.h"
#include "retain_lfs.h"

int writeFlash(uint32_t adr, const char *buf, int len) {
    if (adr & 0x3) {
        logger.error("align error 0x%x", adr);
        return -1;
    }
//    if ((uint32_t) buf & 0x3) {
//        logger.error("buf alignment error");
//        return -2;
//    }
    HAL_FLASH_Unlock();
    for (int j = 0; j < len; j += 4) {
        __HAL_FLASH_CLEAR_FLAG(
                FLASH_FLAG_EOP | FLASH_FLAG_WRPERR);
        uint32_t ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, adr + j, *(uint32_t *) (buf + j));
        switch (ret) {
            case HAL_OK:
                break;
            case HAL_TIMEOUT:
                logger.error("HAL_FLASH_Program() timeout!\n");
                break;
            case HAL_ERROR:
                logger.error("HAL_FLASH_Program() error 0x%08x\n",
                             HAL_FLASH_GetError());
                break;
            default:
                logger.error("HAL_FLASH_Program() returned unknown status %lu\n", ret);
        }
    }
    HAL_FLASH_Lock();
    return 0;
}

void unProtectRWpages(uint32_t sector) {

    static FLASH_OBProgramInitTypeDef OptionsBytesStruct;
    HAL_FLASH_Unlock();

    HAL_FLASH_OB_Unlock();

    HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct);

    OptionsBytesStruct.OptionType = OPTIONBYTE_WRP;

//    OptionsBytesStruct.WRPSector = sector; //// Thx to Artur

    OptionsBytesStruct.WRPState = OB_WRPSTATE_DISABLE;

    if (HAL_FLASHEx_OBProgram(&OptionsBytesStruct) != HAL_OK) {

        //*** Error occurred while options bytes programming. **********************

        // while (1)
        //     ;
    }
    HAL_FLASH_OB_Lock();

    HAL_FLASH_Lock();
}

int erasePage(uint32_t adr, uint32_t pageNrs) {
    FLASH_EraseInitTypeDef f;
    for (uint32_t i = 0; i < pageNrs; i++)
        unProtectRWpages(adr + i);
    HAL_FLASH_Unlock();
    __HAL_FLASH_CLEAR_FLAG(
            FLASH_FLAG_EOP | FLASH_FLAG_WRPERR);
//    f.TypeErase = FLASH_TYPEERASE_SECTORS;
//    f.Banks = FLASH_BANK_1;
//    f.Sector = adr;
//    f.NbSectors = pageNrs;
//    f.VoltageRange = VOLTAGE_RANGE_3;
    //设置PageError
    uint32_t PageError = 0;
    //调用擦除函数
    uint32_t retf = HAL_FLASHEx_Erase(&f, (uint32_t *) &PageError);
    if ((retf != HAL_OK) || (PageError != 0xffffffff)) {
        logger.error("erase error :0x%x\n", PageError);
        return -1;
    }
    HAL_FLASH_Lock();
    return 0;
}

int8_t write_to_backup_sram(const uint8_t *data, uint16_t bytes, uint16_t offset) {
    const uint16_t backup_size = 0x1000;
//    auto *base_addr = (uint8_t *) BKPSRAM_BASE;
    if (bytes + offset >= backup_size) {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
//    __HAL_RCC_BKPSRAM_CLK_ENABLE();
    for (u16 i = 0; i < bytes; i++) {
//        *(base_addr + offset + i) = *(data + i);
    }
//    __HAL_RCC_BKPSRAM_CLK_DISABLE();

    return 0;
}

int8_t read_from_backup_sram(uint8_t *data, uint16_t bytes, uint16_t offset) {
    const uint16_t backup_size = 0x1000;
//    auto *base_addr = (uint8_t *) BKPSRAM_BASE;
    if (bytes + offset >= backup_size) {
        /* ERROR : the last byte is outside the backup SRAM region */
        return -1;
    }
//    __HAL_RCC_BKPSRAM_CLK_ENABLE();
    for (u16 i = 0; i < bytes; i++) {
//        *(data + i) = *(base_addr + offset + i);
    }
//    __HAL_RCC_BKPSRAM_CLK_DISABLE();
    return 0;
}

mem_block *boardBase::get_retain() {
#ifdef USE_LFS
    return new retain_lfs("retain1", "retain2");
#endif
    return nullptr;
}

extern "C"
{
#if USE_TICK_TIM7
TIM_HandleTypeDef htim7;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
 * @brief  This function configures the TIM7 as a time base source.
 *         The time source is configured  to have 1ms time base with a dedicated
 *         Tick interrupt priority.
 * @note   This function is called  automatically at the beginning of program after
 *         reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
 * @param  TickPriority: Tick interrupt priority.
 * @retval HAL status
 */
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    RCC_ClkInitTypeDef clkconfig;
    uint32_t uwTimclock = 0;
    uint32_t uwPrescalerValue = 0;
    uint32_t pFLatency;
    /*Configure the TIM7 IRQ priority */
    HAL_NVIC_SetPriority(TIM7_IRQn, TickPriority, 0);

    /* Enable the TIM7 global Interrupt */
    HAL_NVIC_EnableIRQ(TIM7_IRQn);
    /* Enable TIM7 clock */
    __HAL_RCC_TIM7_CLK_ENABLE();

    /* Get clock configuration */
    HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);

    /* Compute TIM7 clock */
    uwTimclock = 2 * HAL_RCC_GetPCLK1Freq();
    /* Compute the prescaler value to have TIM7 counter clock equal to 1MHz */
    uwPrescalerValue = (uint32_t)((uwTimclock / 1000000U) - 1U);

    /* Initialize TIM7 */
    htim7.Instance = TIM7;

    /* Initialize TIMx peripheral as follow:
    + Period = [(TIM7CLK/1000) - 1]. to have a (1/1000) s time base.
    + Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
    + ClockDivision = 0
    + Counter direction = Up
    */
    htim7.Init.Period = (1000000U / 1000U) - 1U;
    htim7.Init.Prescaler = uwPrescalerValue;
    htim7.Init.ClockDivision = 0;
    htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
    if (HAL_TIM_Base_Init(&htim7) == HAL_OK)
    {
        /* Start the TIM time Base generation in interrupt mode */
        return HAL_TIM_Base_Start_IT(&htim7);
    }

    /* Return function status */
    return HAL_ERROR;
}

/**
 * @brief  Suspend Tick increment.
 * @note   Disable the tick increment by disabling TIM7 update interrupt.
 * @param  None
 * @retval None
 */
void HAL_SuspendTick(void)
{
    /* Disable TIM7 update Interrupt */
    __HAL_TIM_DISABLE_IT(&htim7, TIM_IT_UPDATE);
}

/**
 * @brief  Resume Tick increment.
 * @note   Enable the tick increment by Enabling TIM7 update interrupt.
 * @param  None
 * @retval None
 */
void HAL_ResumeTick(void)
{
    /* Enable TIM7 Update interrupt */
    __HAL_TIM_ENABLE_IT(&htim7, TIM_IT_UPDATE);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    /* USER CODE BEGIN Callback 0 */

    /* USER CODE END Callback 0 */
    if (htim->Instance == TIM7)
    {
        HAL_IncTick();
    }
    /* USER CODE BEGIN Callback 1 */

    /* USER CODE END Callback 1 */
}
void TIM7_IRQHandler(void)
{
    /* USER CODE BEGIN TIM7_IRQn 0 */

    /* USER CODE END TIM7_IRQn 0 */
    HAL_TIM_IRQHandler(&htim7);
    /* USER CODE BEGIN TIM7_IRQn 1 */

    /* USER CODE END TIM7_IRQn 1 */
}
#endif
}
extern "C"
{
int _write(int file, char *data, int len) {
    if ((file != STDOUT_FILENO) && (file != STDERR_FILENO)) {
        errno = EBADF;
        return -1;
    }

    for (int i = 0; i < len; i++) {
        ITM_SendChar(data[i]);
        while (!LL_USART_IsActiveFlag_TXE(USART1));
        LL_USART_TransmitData8(USART1, data[i]);
    }

    // return # of bytes written - as best we can tell
    return len;
}
}

void boardBase::goto_iap() {
    /* RTC handler declaration */
    RTC_HandleTypeDef RtcHandle;
    RtcHandle.Instance = RTC;
    logger.warning("Enter IAP.");
    if (HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0) == 0x1498) {
        __HAL_RCC_PWR_CLK_ENABLE();
        HAL_PWR_EnableBkUpAccess();

        //        __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSE_DIV2);
        __HAL_RCC_RTC_ENABLE();
        HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0, 0x0);

        __HAL_RCC_RTC_DISABLE();
        HAL_PWR_DisableBkUpAccess();
        __HAL_RCC_PWR_CLK_DISABLE();
        void (*SysMemBootJump)();

        __set_MSP(*(__IO uint32_t *) 0x1FFF0000);
        SysMemBootJump = (void (*)()) (*((uint32_t *) 0x1FFF0004));
        SysMemBootJump();
        while (true);
    }
}

int serial_putchar(char c) {
    while (!LL_USART_IsActiveFlag_TXE(USART1));
    LL_USART_TransmitData8(USART1, c);
}

int boardBase::clock_diag() {
    logger.debug("clock= %d \n", HAL_RCC_GetSysClockFreq());
    logger.debug("HCLK = %d \n", HAL_RCC_GetHCLKFreq());
    logger.debug("PCLK1 = %d\n", HAL_RCC_GetPCLK1Freq());
    logger.debug("PCLK2 = %d \n", HAL_RCC_GetPCLK2Freq());
    RCC_OscInitTypeDef RCC_OscInitStruct;
    HAL_RCC_GetOscConfig(&RCC_OscInitStruct);
    if (RCC_OscInitStruct.HSEState == RCC_HSE_ON) {
        logger.debug("HSE ON.\n");
    } else {
        logger.debug("HSE OFF.\n");
    }
    if (RCC_OscInitStruct.LSEState == RCC_LSE_ON) {
        logger.debug("LSE ON");
    } else {
        logger.debug("LSE OFF");
    }
    logger.debug("SysTick : CTRL=0x%x, LOAD= 0x%x, VAL= 0x%x , CALIB= 0x%x\n",
                 (volatile long unsigned int) SysTick->CTRL, (volatile long unsigned int) SysTick->LOAD,
                 (volatile long unsigned int) SysTick->VAL, (volatile long unsigned int) SysTick->CALIB);
    return 0;
}

#define V25 1.43          // from datasheet
#define VSENSE (3.3 / 4096) // VSENSE value
#define Avg_Slope .0043   // 4.3mV from datasheet

void get_cpu_info() {
    rte_data.cpu.vref = __LL_ADC_CALC_VREFANALOG_VOLTAGE(analogRead(AVREF), LL_ADC_RESOLUTION_12B);
    rte_data.cpu.temp = __LL_ADC_CALC_TEMPERATURE(rte_data.cpu.vref, analogRead(ATEMP),
                                                      LL_ADC_RESOLUTION_12B); // formula from datasheet
    rte_data.cpu.vbat =
            __LL_ADC_CALC_DATA_TO_VOLTAGE(rte_data.cpu.vref, analogRead(AVBAT), LL_ADC_RESOLUTION_12B) * 3;
}

#endif
